package userInterface;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.event.MouseInputListener;

public class EditingController
{	
	public EditingController(DrawingView view, DrawingModel model)
	{
		_view = view;
		_model = model;

		_curState = DrawingState.IDLE;
	}

	public void mousePressed(MouseEvent e)
	{
		_pressLocation = new Point(e.getX(), e.getY());
		GeometryDescriptor desc = ((DrawingView)_view).pointGeometry(_pressLocation);
		
		// if the do a left click
		if (e.getButton() == MouseEvent.BUTTON1)
		{
			switch (_curState)
			{
			case IDLE:
			{
				if (desc.getIndex() != -1)
				{
					if (_model.getDrawObject(desc.getIndex()).getShape() == Shape.TEXT)
						_curState = DrawingState.TEXT_SELECTED;
					else
						_curState = DrawingState.OBJECT_SELECTED;

					((DrawingView)_view).setSelectedObject(desc);
					_view.update();
				}
			}
			break;
			case OBJECT_SELECTED:
			{
				if (desc.getIndex() == -1)
				{
					((DrawingView)_view).clearSelectedObject();
					_curState = DrawingState.IDLE;
					_view.update();						
				}
				else if (desc.getControlPoint() != -1)
				{
					_curState = DrawingState.CONTROL_SELECTED;
					if (desc.getIndex() != -1)
						((DrawingView)_view).setSelectedObject(desc);
				}
				else if (_model.getDrawObject(desc.getIndex()).getShape() == Shape.TEXT)
				{
					_curState = DrawingState.TEXT_SELECTED;
					((DrawingView)_view).setSelectedObject(desc);
					_view.update();
				}
				else if (((DrawingView)_view).getSelectedObject().getIndex() != desc.getIndex())
				{
					((DrawingView)_view).setSelectedObject(desc);
					_view.update();
				}
					
			}
			break;
			case TEXT_SELECTED:
			{
				if (desc.getIndex() == -1)
				{
					_curState = DrawingState.IDLE;
					((DrawingView)_view).clearSelectedObject();
					_view.update();
				}
				else if (desc.getControlPoint() != -1)
				{
					_curState = DrawingState.CONTROL_SELECTED;
					((DrawingView)_view).setSelectedObject(desc);
				}
				else if (_model.getDrawObject(desc.getIndex()).getShape() != Shape.TEXT)
				{
					_curState = DrawingState.OBJECT_SELECTED;
					((DrawingView)_view).setSelectedObject(desc);
					_view.update();
				}
				else
				{
					((DrawingView)_view).setSelectedObject(desc);
					_view.update();
				}
			}
			break;
			case CONTROL_SELECTED:
			{
				System.out.println("CONTROL_SELECTED");
				System.out.println("You should only have a control selected when dragging not when pressing");
			}
			break;
			case DRAGGING:
			{
				System.out.println("DRAGGING");
				System.out.println("You should never be dragging when the mouse goes down");
			}
			break;
			case DRAGGING_CONTROL:
			{
				System.out.println("DRAGGING_CONTROL");
				System.out.println("You should never be dragging when the mouse goes down");				
			}
			}
		}
	}

	public void mouseReleased(MouseEvent e)
	{
		if (e.getButton() == MouseEvent.BUTTON1)
		{
			switch(_curState)
			{
			case IDLE:
			{
				// do nothing just a normal click
			}
			break;
			case OBJECT_SELECTED:
			{
				// do nothing just a normal click on an object
			}
			break;
			case TEXT_SELECTED:
			{
				// do nothing just a normal click on a text
			}
			break;
			case CONTROL_SELECTED:
			{
				if (_model.getDrawObject(((DrawingView)_view).getSelectedObject().getIndex()).getShape() == Shape.TEXT)
					_curState = DrawingState.TEXT_SELECTED;
				else
					_curState = DrawingState.OBJECT_SELECTED;
			}
			break;
			case DRAGGING:
			{
				if (_model.getDrawObject(((DrawingView)_view).getSelectedObject().getIndex()).getShape() == Shape.TEXT)
					_curState = DrawingState.TEXT_SELECTED;
				else
					_curState = DrawingState.OBJECT_SELECTED;
				placeObject();
				_view.update();
			}
			break;
			case DRAGGING_CONTROL:
			{
				if (_model.getDrawObject(((DrawingView)_view).getSelectedObject().getIndex()).getShape() == Shape.TEXT)
					_curState = DrawingState.TEXT_SELECTED;
				else
					_curState = DrawingState.OBJECT_SELECTED;
			}
			break;
			}
		}
	}

	public void mouseDragged(MouseEvent e)
	{
		if (e.getModifiers() == MouseEvent.BUTTON1_MASK)
		{
			switch (_curState)
			{
			case IDLE:
				break;
			
			case TEXT_SELECTED:
				if (mouseDragDistance(new Point(e.getX(), e.getY())) >= 3)
				{
					_curState = DrawingState.DRAGGING;
					createTempMovableObj();
				}
				break;
			
			case OBJECT_SELECTED:
				if (mouseDragDistance(new Point(e.getX(), e.getY())) >= 3)
				{
					_curState = DrawingState.DRAGGING;
					createTempMovableObj();
				}
				break;
			
			case CONTROL_SELECTED:
				if (mouseDragDistance(new Point(e.getX(), e.getY())) >= 3)
					_curState = DrawingState.DRAGGING_CONTROL;
				break;
			
			case DRAGGING:
				moveObject(new Point(e.getX(), e.getY()));				
				break;
			
			case DRAGGING_CONTROL:
				dragControl(new Point(e.getX(), e.getY()));
				break;
			}
			
			_lastLocation = new Point(e.getX(), e.getY());				
		}
	}

	public void keyPressed(KeyEvent e)
	{
		switch (_curState)
		{
		case TEXT_SELECTED:
		{
			GeometryDescriptor desc = ((DrawingView)_view).getSelectedObject();
			Text t = ((Text) _model.getDrawObject(desc.getIndex()));
			String s = t.getText();
			String start = s.substring(0, desc.getTextIndex());
			String end = s.substring(desc.getTextIndex());
			
			// typeable characters
			if ((e.getKeyCode() >= 32 && e.getKeyCode() <= 36) || (e.getKeyCode() >= 41 && e.getKeyCode() <= 126) )
			{
				desc.setTextIndex(desc.getTextIndex() + 1);
				t.setText(start + e.getKeyChar() + end);
			}

			// backspace
			else if (e.getKeyCode() == 8)
			{
				if (desc.getTextIndex() == 0)
					break;
				desc.setTextIndex(desc.getTextIndex() - 1);
				t.setText(start.substring(0, start.length() - 1) + end);
			}
			
			// right space
			else if (e.getKeyCode() == 39)
			{
				desc.setTextIndex(min(desc.getTextIndex() + 1, s.length()));
				_view.update();
			}

			// left space
			else if (e.getKeyCode() == 37)
			{
				desc.setTextIndex(max(desc.getTextIndex() - 1, 0));
				_view.update();
			}
			
			// delete
			// left space
			else if (e.getKeyCode() == 127)
			{
				if (desc.getTextIndex() == s.length())
					break;
				t.setText(start + end.substring(1));				
			}

		}
		break;
		
		// for all other cases pressing keys at this point
		//   should not matter. Only for text selected.
		}
	}

	private int max(int a, int b)
	{
		return (a > b) ? a : b;
	}
	
	private int min(int a, int b)
	{
		return (a < b) ? a : b;
	}
	
	private void createTempMovableObj()
	{
		DrawObject obj = _model.getDrawObject(((DrawingView)_view).getSelectedObject().getIndex());
		
		switch (obj.getShape())
		{
		case LINE:
			_draggingClone = new Line((Line) obj);
			break;
		case RECTANGLE:
			_draggingClone = new Rectangle((Rectangle) obj);
			break;
		case ELLIPSE:
			_draggingClone = new Ellipse((Ellipse) obj);
			break;
		case CURVE:
			_draggingClone = new Curve((Curve) obj);
			break;
		case TEXT:
			_draggingClone = new Text((Text) obj);
			break;
		}

		_draggingClone.setColor(_draggingClone.getColor().brighter());
		_model.addDrawObject(_draggingClone);
	}

	private void moveObject(Point p)
	{
		int deltaX = p.X - _lastLocation.X;
		int deltaY = p.Y - _lastLocation.Y;

		_draggingClone.move(deltaX, deltaY);
	}
	
	private void placeObject()
	{
		int deltaX = _lastLocation.X - _pressLocation.X;
		int deltaY = _lastLocation.Y - _pressLocation.Y;
		_model.getDrawObject(((DrawingView)_view).getSelectedObject().getIndex()).move(deltaX, deltaY);
		_model.removeDrawObject(_model.nDrawObjects() - 1);
		_draggingClone = null;
	}
	
	private void dragControl(Point p)
	{
		GeometryDescriptor desc = ((DrawingView)_view).getSelectedObject();
		DrawObject obj = _model.getDrawObject(desc.getIndex());
		switch (obj.getShape())
		{
		case LINE:
		{
			if (desc.getControlPoint() == 0)
				((Line) obj).setStartPoint(p);
			else if (desc.getControlPoint() == 1)
				((Line) obj).setEndPoint(p);
		}
		break;
		case RECTANGLE:
		{
			Rectangle rect = (Rectangle) obj;
			if (desc.getControlPoint() == 0)
			{
				rect.setWidth(rect.getWidth() + rect.getTopLeft().X - p.X);
				rect.setHeight(rect.getHeight() + rect.getTopLeft().Y - p.Y);
				rect.setTopLeft(p);
				if (rect.getWidth() <= 0 && rect.getHeight() <= 0)
					desc.setControlPoint(3);
				else if (rect.getWidth() <= 0)
					desc.setControlPoint(1);
				else if (rect.getHeight() <= 0)
					desc.setControlPoint(2);
			}
			else if (desc.getControlPoint() == 1)
			{
				rect.setWidth(p.X - rect.getTopLeft().X);
				rect.setHeight(rect.getHeight() + rect.getTopLeft().Y - p.Y);
				rect.setTopLeft(new Point(rect.getTopLeft().X, p.Y));
				if (rect.getWidth() <= 0 && rect.getHeight() <= 0)
					desc.setControlPoint(2);
				else if (rect.getWidth() <= 0)
					desc.setControlPoint(0);
				else if (rect.getHeight() <= 0)
					desc.setControlPoint(3);
			}
			else if (desc.getControlPoint() == 2)
			{
				rect.setWidth(rect.getWidth() + rect.getTopLeft().X - p.X);
				rect.setHeight(p.Y - rect.getTopLeft().Y);
				rect.setTopLeft(new Point(p.X, rect.getTopLeft().Y));
				if (rect.getWidth() <= 0 && rect.getHeight() <= 0)
					desc.setControlPoint(1);
				else if (rect.getWidth() <= 0)
					desc.setControlPoint(3);
				else if (rect.getHeight() <= 0)
					desc.setControlPoint(0);
			}
			else if (desc.getControlPoint() == 3)
			{
				rect.setWidth(p.X - rect.getTopLeft().X);
				rect.setHeight(p.Y - rect.getTopLeft().Y);
				if (rect.getWidth() <= 0 && rect.getHeight() <= 0)
					desc.setControlPoint(0);
				else if (rect.getWidth() <= 0)
					desc.setControlPoint(2);
				else if (rect.getHeight() <= 0)
					desc.setControlPoint(1);
			}
		}
		break;
		case ELLIPSE:
		{
			Ellipse elip = (Ellipse) obj;
			if (desc.getControlPoint() == 0)
			{
				elip.setWidth(elip.getWidth() + elip.getTopLeft().X - p.X);
				elip.setHeight(elip.getHeight() + elip.getTopLeft().Y - p.Y);
				elip.setTopLeft(p);
				if (elip.getWidth() <= 0 && elip.getHeight() <= 0)
					desc.setControlPoint(3);
				else if (elip.getWidth() <= 0)
					desc.setControlPoint(1);
				else if (elip.getHeight() <= 0)
					desc.setControlPoint(2);
			}
			else if (desc.getControlPoint() == 1)
			{
				elip.setWidth(p.X - elip.getTopLeft().X);
				elip.setHeight(elip.getHeight() + elip.getTopLeft().Y - p.Y);
				elip.setTopLeft(new Point(elip.getTopLeft().X, p.Y));
				if (elip.getWidth() <= 0 && elip.getHeight() <= 0)
					desc.setControlPoint(2);
				else if (elip.getWidth() <= 0)
					desc.setControlPoint(0);
				else if (elip.getHeight() <= 0)
					desc.setControlPoint(3);
			}
			else if (desc.getControlPoint() == 2)
			{
				elip.setWidth(elip.getWidth() + elip.getTopLeft().X - p.X);
				elip.setHeight(p.Y - elip.getTopLeft().Y);
				elip.setTopLeft(new Point(p.X, elip.getTopLeft().Y));
				if (elip.getWidth() <= 0 && elip.getHeight() <= 0)
					desc.setControlPoint(1);
				else if (elip.getWidth() <= 0)
					desc.setControlPoint(3);
				else if (elip.getHeight() <= 0)
					desc.setControlPoint(0);
			}
			else if (desc.getControlPoint() == 3)
			{
				elip.setWidth(p.X - elip.getTopLeft().X);
				elip.setHeight(p.Y - elip.getTopLeft().Y);
				if (elip.getWidth() <= 0 && elip.getHeight() <= 0)
					desc.setControlPoint(0);
				else if (elip.getWidth() <= 0)
					desc.setControlPoint(2);
				else if (elip.getHeight() <= 0)
					desc.setControlPoint(1);
			}
		}
		break;
		case CURVE:
		{
			if (desc.getControlPoint() == 0)
				((Curve) obj).setStart(p);
			else if (desc.getControlPoint() == 1)
				((Curve) obj).setCtl1(p);
			else if (desc.getControlPoint() == 2)
				((Curve) obj).setCtl2(p);
			else if (desc.getControlPoint() == 3)
				((Curve) obj).setEnd(p);
		}
		break;
		case TEXT:
		{
			if (desc.getControlPoint() == 0)
				((Text) obj).setLocation(p);
		}
		break;
		}
	}
	
	private int mouseDragDistance(Point curMouseLoc)
	{
		return (int) Math.sqrt(Math.pow(curMouseLoc.X - _pressLocation.X, 2) + Math.pow(curMouseLoc.Y - _pressLocation.Y, 2));
	}

	void unfocus()
	{
		_curState = DrawingState.IDLE;
		((DrawingView) _view).clearSelectedObject();
		_view.update();
	}
	
	private UpdateableView _view;
	private DrawingModel _model;
	private DrawingState _curState;
	private Point _pressLocation;
	private Point _lastLocation;
	private DrawObject _draggingClone;
}
